#include "Input.h"

InputDevice::InputDevice() : m_pDevice(NULL), m_x(0), m_y(0) {
	ZeroMemory(m_keyLock, sizeof(bool) * 256);
	ZeroMemory(&m_mouseState, sizeof(DIMOUSESTATE));
	ZeroMemory(m_keyboardState, 256);
	ZeroMemory(m_pressedKeys, 256);
	ZeroMemory(m_pressedButtons, 4);
}

bool InputDevice::Initialize(LPDIRECTINPUT8 pDI, HWND hWnd, DIRECT_INPUT_TYPE type, DWORD cooperativeLevel) {
	// check for a valid parent DIRECTINPUT8 interface
	if(pDI == NULL || type == DIT_FORCE_DWORD)
		return false;
	Release();

	DIDATAFORMAT* pDataFormat;
	m_hWnd = hWnd;
	m_type = type;

	// create the device
	switch(type) {
	case DIT_KEYBOARD:
		if(FAILED(pDI->CreateDevice(GUID_SysKeyboard, &m_pDevice, NULL))) {
			SHOWERROR("Unable to create keyboard device.", __FILE__, __LINE__);
			return false;
		}
		pDataFormat = (DIDATAFORMAT*)&c_dfDIKeyboard;
		break;
	case DIT_MOUSE:
		if(FAILED(pDI->CreateDevice(GUID_SysMouse, &m_pDevice, NULL))) {
			SHOWERROR("Unable to create mouse device.", __FILE__, __LINE__);
			return false;
		}
		pDataFormat = (DIDATAFORMAT*)&c_dfDIMouse;
		break;
	default: 
		return false;
	}

	// set the data format
	if(FAILED(m_pDevice->SetDataFormat(pDataFormat))) {
		SHOWERROR("Unable to set input data format.", __FILE__, __LINE__);
		return false;
	}

	// Set the cooperative level
	if(FAILED(m_pDevice->SetCooperativeLevel(hWnd, cooperativeLevel))) {
		SHOWERROR("Unable to set input cooperative level.", __FILE__, __LINE__);
		return false;
	}

	// Acquire the device
	if(FAILED(m_pDevice->Acquire())) {
		SHOWERROR("Unable to acquire the input device.", __FILE__, __LINE__);
		return false;
	}

	return true;
}

void InputDevice::Read() {
	if(!m_pDevice)
		return;
    
	// grab the data 
	if(m_type == DIT_MOUSE) {
		HRESULT hr = m_pDevice->GetDeviceState(sizeof(DIMOUSESTATE), (LPVOID)&m_mouseState);
		if(FAILED(hr)) {
			if(hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED) {
				// device is lost, try to reaquire it
				m_pDevice->Acquire();
			}
			return;
		}
		// store cursor position
		POINT pt;
		GetCursorPos(&pt);
		ScreenToClient(m_hWnd, &pt);
		m_x = pt.x;
		m_y = pt.y;
		// get pressed keys
		for(int i = 0; i < 4; i++) {
			if(m_mouseState.rgbButtons[i] & 0x80) {
				m_pressedButtons[i] = true;
			} else {
				m_pressedButtons[i] = false;
			}
		}
	} else if(m_type == DIT_KEYBOARD) {
		HRESULT hr = m_pDevice->GetDeviceState(256, (LPVOID)&m_keyboardState);
		if(FAILED(hr)) {
			if(hr == DIERR_INPUTLOST || hr == DIERR_NOTACQUIRED) {
				// device is lost, try to reaquire it
				m_pDevice->Acquire();
			}
			return;
		}
		// get pressed keys and release locks on key up
		for(int i=0; i<256; i++) {
			if(!(m_keyboardState[i] & 0x80)) {
				// key is up so release lock
				m_keyLock[i] = false;
				m_pressedKeys[i] = false;
			} else {
				// key is pressed if it isn't locked
				m_pressedKeys[i] = !(m_keyLock[i]);
			}
		}
	}
}

// locks a key so it is only read once per key down.
void InputDevice::LockKey(DWORD key) {
	m_keyLock[key] = true;
}

void InputDevice::Release() {
	if(m_pDevice) {
		m_pDevice->Unacquire();
		SAFE_RELEASE(m_pDevice);
	}
}
